package connectionPool;

import java.sql.SQLException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static java.lang.System.currentTimeMillis;

public class ConnectionPool {

	private final int pool_size = 4;
	private final long ideal_timeout = 1000 * 40;
	private final long waiting_timeout = 1000 * 10;
	private final String driver = "oracle.jdbc.driver.OracleDriver";

	private final MyConnection[] connections;
	private final Semaphore semaphore;

	public ConnectionPool(String url, String user, String passwd)
			throws SQLException {
		semaphore = new Semaphore(pool_size);
		connections = new MyConnection[pool_size];
		loadDriver();
		initPool(url, user, passwd);
		scheduleConnectionChecker();
		System.out.println("Pool created");
	}

	private void loadDriver() {
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	private void scheduleConnectionChecker() {
		Timer t = new Timer();
		ConnectionChecker scannerTask = new ConnectionChecker();
		t.scheduleAtFixedRate(scannerTask, 0, 10 * 1000);
	}

	private void initPool(String url, String user, String passwd)
			throws SQLException {
		for (int i = 0; i < connections.length; i++) {
			connections[i] = new MyConnection(url, user, passwd);
		}
	}

	public MyConnection checkout() throws InterruptedException,
			TimeoutException {
		if (semaphore.tryAcquire(waiting_timeout, TimeUnit.MILLISECONDS)) {
			MyConnection con;
			for (int i = 0; i < connections.length; i++) {
				con = connections[i];
				if (!con.isInUse()) {
					con.setInUse(true);
					con.updateUsageTime();
					System.out.println(con + " checkout!");
					return con;
				}
			}
		} else
			throw new TimeoutException("Waiting time is over");
		return null;
	}

	public void checkin(MyConnection con) {
		if (con.isInUse()) {
			System.out.println(con + " returned");
			con.setInUse(false);
			semaphore.release();
		}
	}

	public void close() {
		for (int i = 0; i < connections.length; i++)
			connections[i].close();
	}

	private class ConnectionChecker extends TimerTask {
		@Override
		public void run() {
			for (int i = 0; i < connections.length; i++) {
				if (connections[i].isInUse()) {
					if (currentTimeMillis() - connections[i].getLastUsageTime() > ideal_timeout) {
						checkin(connections[i]);
					}
				}
			}
		}
	}
}
